package gobase

/**
  DElement
      + RemoveFromList()
*/

// DElement is an DElement of a linked DList.
type DElement struct {
	// Next and previous pointers in the doubly-linked DList of DElements.
	// To simplify the implementation, internally a DList l is implemented
	// as a ring, such that &l.root is both the next DElement of the last
	// DList DElement (l.Back()) and the previous DElement of the first DList
	// DElement (l.Front()).
	next, prev *DElement

	// The DList to which this DElement belongs.
	DList *DList

	// The value stored with this DElement.
	Value interface{}
}

// Next returns the next DList DElement or nil.
func (e *DElement) Next() *DElement {
	if p := e.next; e.DList != nil && p != &e.DList.root {
		return p
	}
	return nil
}

// Prev returns the previous DList DElement or nil.
func (e *DElement) Prev() *DElement {
	if p := e.prev; e.DList != nil && p != &e.DList.root {
		return p
	}
	return nil
}

func (e *DElement) RemoveFromDList() {
	if e.DList != nil {
		e.DList.remove(e)
	}
}

// DList represents a doubly linked DList.
// The zero value for DList is an empty DList ready to use.
type DList struct {
	root DElement // sentinel DList DElement, only &root, root.prev, and root.next are used
	len  int      // cursor DList length excluding (this) sentinel DElement
}

// Init initializes or clears DList l.
func (l *DList) Init() *DList {
	l.root.next = &l.root
	l.root.prev = &l.root
	l.len = 0
	return l
}

// New returns an initialized DList.
func NewDList() *DList { return new(DList).Init() }

// Len returns the number of DElements of DList l.
// The complexity is O(1).
func (l *DList) Len() int { return l.len }

// Front returns the first DElement of DList l or nil.
func (l *DList) Front() *DElement {
	if l.len == 0 {
		return nil
	}
	return l.root.next
}

// Back returns the last DElement of DList l or nil.
func (l *DList) Back() *DElement {
	if l.len == 0 {
		return nil
	}
	return l.root.prev
}

// lazyInit lazily initializes a zero DList value.
func (l *DList) lazyInit() {
	if l.root.next == nil {
		l.Init()
	}
}

// insert inserts e after at, increments l.len, and returns e.
func (l *DList) insert(e, at *DElement) *DElement {
	n := at.next
	at.next = e
	e.prev = at
	e.next = n
	n.prev = e
	e.DList = l
	l.len++
	return e
}

// insertValue is a convenience wrapper for insert(&DElement{Value: v}, at).
func (l *DList) insertValue(v interface{}, at *DElement) *DElement {
	return l.insert(&DElement{Value: v}, at)
}

// remove removes e from its DList, decrements l.len, and returns e.
func (l *DList) remove(e *DElement) *DElement {
	e.prev.next = e.next
	e.next.prev = e.prev
	e.next = nil // avoid memory leaks
	e.prev = nil // avoid memory leaks
	e.DList = nil
	l.len--
	return e
}

// Remove removes e from l if e is an DElement of DList l.
// It returns the DElement value e.Value.
func (l *DList) Remove(e *DElement) interface{} {
	if e.DList == l {
		// if e.DList == l, l must have been initialized when e was inserted
		// in l or l == nil (e is a zero DElement) and l.remove will crash
		l.remove(e)
	}
	return e.Value
}

// PushFront inserts a new DElement e with value v at the front of DList l and returns e.
func (l *DList) PushFront(v interface{}) *DElement {
	l.lazyInit()
	return l.insertValue(v, &l.root)
}

// PushBack inserts a new DElement e with value v at the back of DList l and returns e.
func (l *DList) PushBack(v interface{}) *DElement {
	l.lazyInit()
	return l.insertValue(v, l.root.prev)
}

// InsertBefore inserts a new DElement e with value v immediately before mark and returns e.
// If mark is not an DElement of l, the DList is not modified.
func (l *DList) InsertBefore(v interface{}, mark *DElement) *DElement {
	if mark.DList != l {
		return nil
	}
	// see comment in DList.Remove about initialization of l
	return l.insertValue(v, mark.prev)
}

// InsertAfter inserts a new DElement e with value v immediately after mark and returns e.
// If mark is not an DElement of l, the DList is not modified.
func (l *DList) InsertAfter(v interface{}, mark *DElement) *DElement {
	if mark.DList != l {
		return nil
	}
	// see comment in DList.Remove about initialization of l
	return l.insertValue(v, mark)
}

// MoveToFront moves DElement e to the front of DList l.
// If e is not an DElement of l, the DList is not modified.
func (l *DList) MoveToFront(e *DElement) {
	if e.DList != l || l.root.next == e {
		return
	}
	// see comment in DList.Remove about initialization of l
	l.insert(l.remove(e), &l.root)
}

// MoveToBack moves DElement e to the back of DList l.
// If e is not an DElement of l, the DList is not modified.
func (l *DList) MoveToBack(e *DElement) {
	if e.DList != l || l.root.prev == e {
		return
	}
	// see comment in DList.Remove about initialization of l
	l.insert(l.remove(e), l.root.prev)
}

// MoveBefore moves DElement e to its new position before mark.
// If e or mark is not an DElement of l, or e == mark, the DList is not modified.
func (l *DList) MoveBefore(e, mark *DElement) {
	if e.DList != l || e == mark || mark.DList != l {
		return
	}
	l.insert(l.remove(e), mark.prev)
}

// MoveAfter moves DElement e to its new position after mark.
// If e or mark is not an DElement of l, or e == mark, the DList is not modified.
func (l *DList) MoveAfter(e, mark *DElement) {
	if e.DList != l || e == mark || mark.DList != l {
		return
	}
	l.insert(l.remove(e), mark)
}

// PushBackDList inserts a copy of an other DList at the back of DList l.
// The DLists l and other may be the same.
func (l *DList) PushBackDList(other *DList) {
	l.lazyInit()
	for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
		l.insertValue(e.Value, l.root.prev)
	}
}

// PushFrontDList inserts a copy of an other DList at the front of DList l.
// The DLists l and other may be the same.
func (l *DList) PushFrontDList(other *DList) {
	l.lazyInit()
	for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
		l.insertValue(e.Value, &l.root)
	}
}
